# ColumnBinderRegistry.java

    package org.codefilarete.stalactite.sql.statement.binder;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;

    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.statement.SQLStatement.BindingException;

    /**
     * Registry of {@link ParameterBinder}s per {@link Column} and {@link Class}.
     * See {@link #register(Column, ParameterBinder)} and {@link #register(Class, ParameterBinder)} to specify binder for a column or type.
     *
     * @author Guillaume Mary
     */
    public class ColumnBinderRegistry
    		implements ParameterBinderIndex<Column, ParameterBinder>,
    		ResultSetReaderRegistry,
    		PreparedStatementWriterRegistry {

    	/**
    	 * Registry for {@link Column}s
    	 */
    	private final Map<Column, ParameterBinder> bindersPerColumn = new HashMap<>();

    	private final ParameterBinderRegistry parameterBinderRegistry;

    	public ColumnBinderRegistry() {
    		this(new ParameterBinderRegistry());
    	}

    	public ColumnBinderRegistry(ParameterBinderRegistry parameterBinderRegistry) {
    		this.parameterBinderRegistry = parameterBinderRegistry;
    	}

    	public <T> void register(Class<T> clazz, ParameterBinder<T> parameterBinder) {
    		this.parameterBinderRegistry.register(clazz, parameterBinder);
    	}

    	public <T> void register(Column column, ParameterBinder<T> parameterBinder) {
    		ParameterBinder existingBinder = this.bindersPerColumn.get(column);
    		if (existingBinder != null && !existingBinder.equals(parameterBinder)) {
    			throw new BindingException("Binder for column " + column + " already exists");
    		}
    		this.bindersPerColumn.put(column, parameterBinder);
    	}

    	/**
    	 * Gives the registered {@link ParameterBinder} for the given type.
    	 *
    	 * @param clazz a class
    	 * @return the registered {@link ParameterBinder} for the given type
    	 * @throws BindingException if the binder doesn't exist
    	 */
    	public <T> ParameterBinder<T> getBinder(Class<T> clazz) {
    		return parameterBinderRegistry.getBinder(clazz);
    	}

    	/**
    	 * Gives the {@link ParameterBinder} of a column if exists, else gives it for the Java type of the {@link Column}
    	 *
    	 * @param column any non null {@link Column}
    	 * @return the binder for the column or for its Java type
    	 * @throws BindingException if the binder doesn't exist
    	 */
    	@Override
    	public ParameterBinder doGetBinder(Column column) {
    		ParameterBinder columnBinder = bindersPerColumn.get(column);
    		return columnBinder != null ? columnBinder : getBinder(column.getJavaType());
    	}

    	@Override
    	public Set<Column> keys() {
    		return bindersPerColumn.keySet();
    	}

    	@Override
    	public Set<Entry<Column, ParameterBinder>> all() {
    		return bindersPerColumn.entrySet();
    	}

    	@Override
    	public <T> ResultSetReader<T> doGetReader(Class<T> key) {
    		// since ParameterBinder is also a ResultSetReader we just return it
    		return getBinder(key);
    	}

    	@Override
    	public <T> PreparedStatementWriter<T> doGetWriter(Class<T> key) {
    		// since ParameterBinder is also a PreparedStatementWriter we just return it
    		return getBinder(key);
    	}
    }